home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: Alpha / Whiteline Alpha.iso / linux / atari / source / source.lzh / atari-linux-0.01pl3 / fs / ext2 / truncate.c < prev   
Encoding:
C/C++ Source or Header  |  1994-06-05  |  8.0 KB  |  337 lines

  1. /*
  2.  *  linux/fs/ext2/truncate.c
  3.  *
  4.  *  Copyright (C) 1992, 1993  Remy Card (card@masi.ibp.fr)
  5.  *
  6.  *  from
  7.  *
  8.  *  linux/fs/minix/truncate.c
  9.  *
  10.  *  Copyright (C) 1991, 1992  Linus Torvalds
  11.  */
  12.  
  13. #include <linux/errno.h>
  14. #include <linux/fs.h>
  15. #include <linux/ext2_fs.h>
  16. #include <linux/fcntl.h>
  17. #include <linux/sched.h>
  18. #include <linux/stat.h>
  19. #include <linux/locks.h>
  20.  
  21. #if defined (__i386__)
  22. /* can gcc *really* not optimize the following C version sufficiently
  23.  * enough that it could *not* be inline assembler?    Hamish Macdonald
  24.  */
  25. #define clear_block(addr,size,value) \
  26.     __asm__("cld\n\t" \
  27.         "rep\n\t" \
  28.         "stosl" \
  29.         : \
  30.         :"a" (value), "c" (size / 4), "D" ((long) (addr)) \
  31.         :"cx", "di")
  32. #else
  33. #define clear_block(addr,size,value) {          \
  34.     int i;                      \
  35.     unsigned long *p = (unsigned long *)addr; \
  36.     for (i = 0; i < size/4; i++)          \
  37.     *p++ = (unsigned long)value;          \
  38. }
  39. #endif
  40.  
  41. /*
  42.  * Truncate has the most races in the whole filesystem: coding it is
  43.  * a pain in the a**. Especially as I don't do any locking...
  44.  *
  45.  * The code may look a bit weird, but that's just because I've tried to
  46.  * handle things like file-size changes in a somewhat graceful manner.
  47.  * Anyway, truncating a file at the same time somebody else writes to it
  48.  * is likely to result in pretty weird behaviour...
  49.  *
  50.  * The new code handles normal truncates (size = 0) as well as the more
  51.  * general case (size = XXX). I hope.
  52.  */
  53.  
  54. static int trunc_direct (struct inode * inode)
  55. {
  56.     int i, tmp;
  57.     unsigned long * p;
  58.     struct buffer_head * bh;
  59.     int retry = 0;
  60.     int blocks = inode->i_sb->s_blocksize / 512;
  61. #define DIRECT_BLOCK ((inode->i_size + inode->i_sb->s_blocksize - 1) / \
  62.             inode->i_sb->s_blocksize)
  63.     int direct_block = DIRECT_BLOCK;
  64.  
  65. repeat:
  66.     for (i = direct_block ; i < EXT2_NDIR_BLOCKS ; i++) {
  67.         p = inode->u.ext2_i.i_data + i;
  68.         tmp = *p;
  69.         if (!tmp)
  70.             continue;
  71.         if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
  72.             bh = getblk (inode->i_dev, tmp,
  73.                      inode->i_sb->s_blocksize);
  74.         else
  75.             bh = get_hash_table (inode->i_dev, tmp,
  76.                          inode->i_sb->s_blocksize);
  77.         if (i < direct_block) {
  78.             brelse (bh);
  79.             goto repeat;
  80.         }
  81.         if ((bh && bh->b_count != 1) || tmp != *p) {
  82.             retry = 1;
  83.             brelse (bh);
  84.             continue;
  85.         }
  86.         *p = 0;
  87.         inode->i_blocks -= blocks;
  88.         inode->i_dirt = 1;
  89.         if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
  90.             clear_block (bh->b_data, inode->i_sb->s_blocksize,
  91.                      CURRENT_TIME);
  92.             bh->b_dirt = 1;
  93.         }
  94.         brelse (bh);
  95.         ext2_free_block (inode->i_sb, tmp);
  96.     }
  97.     return retry;
  98. }
  99.  
  100. static int trunc_indirect (struct inode * inode, int offset, unsigned long * p)
  101. {
  102.     int i, tmp;
  103.     struct buffer_head * bh;
  104.     struct buffer_head * ind_bh;
  105.     unsigned long * ind;
  106.     int retry = 0;
  107.     int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
  108.     int blocks = inode->i_sb->s_blocksize / 512;
  109. #define INDIRECT_BLOCK ((int)DIRECT_BLOCK - offset)
  110.     int indirect_block = INDIRECT_BLOCK;
  111.  
  112.     tmp = *p;
  113.     if (!tmp)
  114.         return 0;
  115.     ind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
  116.     if (tmp != *p) {
  117.         brelse (ind_bh);
  118.         return 1;
  119.     }
  120.     if (!ind_bh) {
  121.         *p = 0;
  122.         return 0;
  123.     }
  124. repeat:
  125.     for (i = indirect_block ; i < addr_per_block ; i++) {
  126.         if (i < 0)
  127.             i = 0;
  128.         if (i < indirect_block)
  129.             goto repeat;
  130.         ind = i + (unsigned long *) ind_bh->b_data;
  131.         tmp = *ind;
  132.         if (!tmp)
  133.             continue;
  134.         if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL)
  135.             bh = getblk (inode->i_dev, tmp,
  136.                      inode->i_sb->s_blocksize);
  137.         else
  138.             bh = get_hash_table (inode->i_dev, tmp,
  139.                          inode->i_sb->s_blocksize);
  140.         if (i < indirect_block) {
  141.             brelse (bh);
  142.             goto repeat;
  143.         }
  144.         if ((bh && bh->b_count != 1) || tmp != *ind) {
  145.             retry = 1;
  146.             brelse (bh);
  147.             continue;
  148.         }
  149.         *ind = 0;
  150.         ind_bh->b_dirt = 1;
  151.         if (inode->u.ext2_i.i_flags & EXT2_SECRM_FL) {
  152.             clear_block (bh->b_data, inode->i_sb->s_blocksize,
  153.                      CURRENT_TIME);
  154.             bh->b_dirt = 1;
  155.         }
  156.         brelse (bh);
  157.         ext2_free_block (inode->i_sb, tmp);
  158.         inode->i_blocks -= blocks;
  159.         inode->i_dirt = 1;
  160.     }
  161.     ind = (unsigned long *) ind_bh->b_data;
  162.     for (i = 0; i < addr_per_block; i++)
  163.         if (*(ind++))
  164.             break;
  165.     if (i >= addr_per_block)
  166.         if (ind_bh->b_count != 1)
  167.             retry = 1;
  168.         else {
  169.             tmp = *p;
  170.             *p = 0;
  171.             inode->i_blocks -= blocks;
  172.             inode->i_dirt = 1;
  173.             ext2_free_block (inode->i_sb, tmp);
  174.         }
  175.     if (IS_SYNC(inode) && ind_bh->b_dirt) {
  176.         ll_rw_block (WRITE, 1, &ind_bh);
  177.         wait_on_buffer (ind_bh);
  178.     }
  179.     brelse (ind_bh);
  180.     return retry;
  181. }
  182.  
  183. static int trunc_dindirect (struct inode * inode, int offset,
  184.                 unsigned long * p)
  185. {
  186.     int i, tmp;
  187.     struct buffer_head * dind_bh;
  188.     unsigned long * dind;
  189.     int retry = 0;
  190.     int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
  191.     int blocks = inode->i_sb->s_blocksize / 512;
  192. #define DINDIRECT_BLOCK (((int)DIRECT_BLOCK - offset) / addr_per_block)
  193.     int dindirect_block = DINDIRECT_BLOCK;
  194.  
  195.     tmp = *p;
  196.     if (!tmp)
  197.         return 0;
  198.     dind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
  199.     if (tmp != *p) {
  200.         brelse (dind_bh);
  201.         return 1;
  202.     }
  203.     if (!dind_bh) {
  204.         *p = 0;
  205.         return 0;
  206.     }
  207. repeat:
  208.     for (i = dindirect_block ; i < addr_per_block ; i++) {
  209.         if (i < 0)
  210.             i = 0;
  211.         if (i < dindirect_block)
  212.             goto repeat;
  213.         dind = i + (unsigned long *) dind_bh->b_data;
  214.         tmp = *dind;
  215.         if (!tmp)
  216.             continue;
  217.         retry |= trunc_indirect (inode, offset + (i * addr_per_block),
  218.                       dind);
  219.         dind_bh->b_dirt = 1;
  220.     }
  221.     dind = (unsigned long *) dind_bh->b_data;
  222.     for (i = 0; i < addr_per_block; i++)
  223.         if (*(dind++))
  224.             break;
  225.     if (i >= addr_per_block)
  226.         if (dind_bh->b_count != 1)
  227.             retry = 1;
  228.         else {
  229.             tmp = *p;
  230.             *p = 0;
  231.             inode->i_blocks -= blocks;
  232.             inode->i_dirt = 1;
  233.             ext2_free_block (inode->i_sb, tmp);
  234.         }
  235.     if (IS_SYNC(inode) && dind_bh->b_dirt) {
  236.         ll_rw_block (WRITE, 1, &dind_bh);
  237.         wait_on_buffer (dind_bh);
  238.     }
  239.     brelse (dind_bh);
  240.     return retry;
  241. }
  242.  
  243. static int trunc_tindirect (struct inode * inode)
  244. {
  245.     int i, tmp;
  246.     struct buffer_head * tind_bh;
  247.     unsigned long * tind, * p;
  248.     int retry = 0;
  249.     int addr_per_block = EXT2_ADDR_PER_BLOCK(inode->i_sb);
  250.     int blocks = inode->i_sb->s_blocksize / 512;
  251. #define TINDIRECT_BLOCK (((int)DIRECT_BLOCK - (addr_per_block * addr_per_block + \
  252.               addr_per_block + EXT2_NDIR_BLOCKS)) / \
  253.               (addr_per_block * addr_per_block))
  254.     int tindirect_block = TINDIRECT_BLOCK;
  255.  
  256.     p = inode->u.ext2_i.i_data + EXT2_TIND_BLOCK;
  257.     if (!(tmp = *p))
  258.         return 0;
  259.     tind_bh = bread (inode->i_dev, tmp, inode->i_sb->s_blocksize);
  260.     if (tmp != *p) {
  261.         brelse (tind_bh);
  262.         return 1;
  263.     }
  264.     if (!tind_bh) {
  265.         *p = 0;
  266.         return 0;
  267.     }
  268. repeat:
  269.     for (i = tindirect_block ; i < addr_per_block ; i++) {
  270.         if (i < 0)
  271.             i = 0;
  272.         if (i < tindirect_block)
  273.             goto repeat;
  274.         tind = i + (unsigned long *) tind_bh->b_data;
  275.         retry |= trunc_dindirect(inode, EXT2_NDIR_BLOCKS +
  276.             addr_per_block + (i + 1) * addr_per_block * addr_per_block,
  277.             tind);
  278.         tind_bh->b_dirt = 1;
  279.     }
  280.     tind = (unsigned long *) tind_bh->b_data;
  281.     for (i = 0; i < addr_per_block; i++)
  282.         if (*(tind++))
  283.             break;
  284.     if (i >= addr_per_block)
  285.         if (tind_bh->b_count != 1)
  286.             retry = 1;
  287.         else {
  288.             tmp = *p;
  289.             *p = 0;
  290.             inode->i_blocks -= blocks;
  291.             inode->i_dirt = 1;
  292.             ext2_free_block (inode->i_sb, tmp);
  293.         }
  294.     if (IS_SYNC(inode) && tind_bh->b_dirt) {
  295.         ll_rw_block (WRITE, 1, &tind_bh);
  296.         wait_on_buffer (tind_bh);
  297.     }
  298.     brelse (tind_bh);
  299.     return retry;
  300. }
  301.         
  302. void ext2_truncate (struct inode * inode)
  303. {
  304.     int retry;
  305.  
  306.     if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) ||
  307.         S_ISLNK(inode->i_mode)))
  308.         return;
  309.     while (1) {
  310.         retry = trunc_direct(inode);
  311.         retry |= trunc_indirect (inode, EXT2_IND_BLOCK,
  312.             (unsigned long *) &inode->u.ext2_i.i_data[EXT2_IND_BLOCK]);
  313.         retry |= trunc_dindirect (inode, EXT2_IND_BLOCK +
  314.             EXT2_ADDR_PER_BLOCK(inode->i_sb),
  315.             (unsigned long *) &inode->u.ext2_i.i_data[EXT2_DIND_BLOCK]);
  316.         retry |= trunc_tindirect (inode);
  317.         if (!retry)
  318.             break;
  319.         if (IS_SYNC(inode) && inode->i_dirt)
  320.             ext2_sync_inode (inode);
  321.         current->counter = 0;
  322.         schedule ();
  323.     }
  324.     inode->i_mtime = inode->i_ctime = CURRENT_TIME;
  325.     inode->i_dirt = 1;
  326. }
  327.  
  328. /*
  329.  * Called when a inode is released. Note that this is different
  330.  * from ext2_open: open gets called at every open, but release
  331.  * gets called only when /all/ the files are closed.
  332.  */
  333. void ext2_release (struct inode * inode, struct file * filp)
  334. {
  335.     printk ("ext2_release not implemented\n");
  336. }
  337.